/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view;

import android.annotation.IntDef;
import android.content.res.CompatibilityInfo.Translator;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import dalvik.system.CloseGuard;

/**
 * 处理由屏幕合成器管理的原始缓冲区
 */
public class Surface implements Parcelable {
    private static final String TAG = "Surface";

    private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
            throws OutOfResourcesException;
    private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);

    private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
            throws OutOfResourcesException;
    private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);

    private static native void nativeRelease(long nativeObject);
    private static native boolean nativeIsValid(long nativeObject);
    private static native boolean nativeIsConsumerRunningBehind(long nativeObject);
    private static native long nativeReadFromParcel(long nativeObject, Parcel source);
    private static native void nativeWriteToParcel(long nativeObject, Parcel dest);

    private static native void nativeAllocateBuffers(long nativeObject);

    public static final Parcelable.Creator<Surface> CREATOR =
            new Parcelable.Creator<Surface>() {
        @Override
        public Surface createFromParcel(Parcel source) {
            try {
                Surface s = new Surface();
                s.readFromParcel(source);
                return s;
            } catch (Exception e) {
                Log.e(TAG, "Exception creating surface from parcel", e);
                return null;
            }
        }

        @Override
        public Surface[] newArray(int size) {
            return new Surface[size];
        }
    };

    private final CloseGuard mCloseGuard = CloseGuard.get();

    // Guarded state：守卫状态。
    final Object mLock = new Object(); // 保护原生状态
    private String mName;
    long mNativeObject; // 软件包仅适用于SurfaceControl访问
    private long mLockedObject;
    private int mGenerationId; // 每当Native对象改变时就增加
    private final Canvas mCanvas = new CompatibleCanvas();

    // 用于缩放由应用程序设置的矩阵的矩阵。 对于非兼容模式，这设置为空。
    private Matrix mCompatibleMatrix;

    /** @hide */
    @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Rotation {}

    /**
     * 旋转常数：0度旋转（自然方向）
     */
    public static final int ROTATION_0 = 0;

    /**
     * 旋转常数：90度旋转。
     */
    public static final int ROTATION_90 = 1;

    /**
     * 旋转常数：180度旋转。
     */
    public static final int ROTATION_180 = 2;

    /**
     * 旋转常数：270度旋转
     */
    public static final int ROTATION_270 = 3;

    /**
     * ：创建一个空surface，稍后通过readFromParcel().填充。
     * @hide
     */
    public Surface() {
    }

    /**
     * Create Surface from a {@link SurfaceTexture}.
     *
     * 绘制到Surface上的图像将被提供给{@link SurfaceTexture}，
     * 它可以通过{@link SurfaceTexture＃update Tex Image}将它们附加到OpenGL ES纹理。
     * 
     *
     * @param surfaceTexture 由此Surface更新的{@link SurfaceTexture}。
     * @throws OutOfResourcesException 如果surface无法创建
     */
    public Surface(SurfaceTexture surfaceTexture) {
        if (surfaceTexture == null) {
            throw new IllegalArgumentException("surfaceTexture must not be null");
        }

        synchronized (mLock) {
            mName = surfaceTexture.toString();
            setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
        }
    }

    /* called from android_view_Surface_createFromIGraphicBufferProducer() */
    private Surface(long nativeObject) {
        synchronized (mLock) {
            setNativeObjectLocked(nativeObject);
        }
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (mCloseGuard != null) {
                mCloseGuard.warnIfOpen();
            }
            release();
        } finally {
            super.finalize();
        }
    }

    /**
     * 释放对服务器端表面的本地引用。 当你完成一个Surface时总是调用release（）。 这会使surface无效。
     */
    public void release() {
        synchronized (mLock) {
            if (mNativeObject != 0) {
                nativeRelease(mNativeObject);
                setNativeObjectLocked(0);
            }
        }
    }

    /**
     * 释放与此表面关联的所有服务器端状态并释放此对象的引用。 该方法只能从创建服务的进程中调用。
     * @hide
     */
    public void destroy() {
        release();
    }

    /**
     * 如果此对象包含有效surface，则返回true
     *
     * @return 如果它拥有物理表面，则为真，因此锁定Canvas（）将成功。 否则返回false。
     */
    public boolean isValid() {
        synchronized (mLock) {
            if (mNativeObject == 0) return false;
            return nativeIsValid(mNativeObject);
        }
    }

    /**
     * 获取此曲面的世代数，每次包含在该对象中的本地曲面发生变化时递增。
     *
     * @return The current generation number.
     * @hide
     */
    public int getGenerationId() {
        synchronized (mLock) {
            return mGenerationId;
        }
    }

    /**
     * 如果Surface的使用者在生产者后面运行，则返回true。
     *
     * @return 如果消费者在生产者之前多于一个缓冲区，则为真。
     * @hide
     */
    public boolean isConsumerRunningBehind() {
        synchronized (mLock) {
            checkNotReleasedLocked();
            return nativeIsConsumerRunningBehind(mNativeObject);
        }
    }

    /**
     *获取{@link Canvas}以绘制到这个表面。
     * 
     * 绘制到提供的{@link Canvas}后，调用者必须调用{@link #unlockCanvasAndPost}将新内容发布到曲面。
     *
     * @param inOutDirty 表示调用者想要重绘的脏区域的矩形。
     *                  例如，如果曲面已被调整大小或曲面的先前内容不可用，
     *                   此功能可以选择展开脏矩形。
     *                   调用者必须在从此函数返回时重新绘制整个脏区域，如Out Dirty矩形的内容所示。
     *                   调用者也可以传递null，在整个表面应该重绘的情况下。
     *
     * @return 用于绘制表面的画布
     *
     * @throws IllegalArgumentException If the inOutDirty rectangle is not valid.
     * @throws OutOfResourcesException 如果画布无法锁定。
     */
    public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException {
        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mLockedObject != 0) {
                //理想情况下，nativeLockCanvas()会引发这种情况并阻止双重锁定，
                // 但如果m Native Object已更新，则不会发生这种情况。
                // 我们不能放弃旧的mLockedObject ，因为它可能仍在使用中，所以我们只是拒绝重新锁定Surface。

                throw new IllegalArgumentException("Surface was already locked");
            }
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        }
    }

    /**
     * 将{@link Canvas}的新内容发布到表面并释放{@link Canvas}。     *
     * @param canvas 以前从{@link #lockCanvas}获取的画布
     */
    public void unlockCanvasAndPost(Canvas canvas) {
        if (canvas != mCanvas) {
            throw new IllegalArgumentException("canvas object must be the same instance that "
                    + "was previously returned by lockCanvas");
        }

        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mNativeObject != mLockedObject) {
                Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
                        Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
                        Long.toHexString(mLockedObject) +")");
            }
            if (mLockedObject == 0) {
                throw new IllegalStateException("Surface was not locked");
            }
            try {
                nativeUnlockCanvasAndPost(mLockedObject, canvas);
            } finally {
                nativeRelease(mLockedObject);
                mLockedObject = 0;
            }
        }
    }

    /**
     * @deprecated 此API已被删除，不受支持。 不使用。
     */
    @Deprecated
    public void unlockCanvas(Canvas canvas) {
        throw new UnsupportedOperationException();
    }

    /**
     * 设置用于在兼容模式下缩放画布宽度/高度的翻译器。
     */
    void setCompatibilityTranslator(Translator translator) {
        if (translator != null) {
            float appScale = translator.applicationScale;
            mCompatibleMatrix = new Matrix();
            mCompatibleMatrix.setScale(appScale, appScale);
        }
    }

    /**
     * 复制另一个表面到这个。 这个表面现在持有与原始表面相同的数据的参考，并且不是所有者。
     * 这是窗口管理器在从客户端返回窗口表面时使用的，它将窗口管理器管理的表示转换为客户端用于绘制的表示。
     * @hide
     */
    public void copyFrom(SurfaceControl other) {
        if (other == null) {
            throw new IllegalArgumentException("other must not be null");
        }

        long surfaceControlPtr = other.mNativeObject;
        if (surfaceControlPtr == 0) {
            throw new NullPointerException(
                    "SurfaceControl native object is null. Are you using a released SurfaceControl?");
        }
        long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);

        synchronized (mLock) {
            if (mNativeObject != 0) {
                nativeRelease(mNativeObject);
            }
            setNativeObjectLocked(newNativeObject);
        }
    }

    /**
     * 这仅适用于 {@link SurfaceView#updateWindow}
     * @param other 访问不是线程安全的
     * @hide
     * @deprecated
     */
    @Deprecated
    public void transferFrom(Surface other) {
        if (other == null) {
            throw new IllegalArgumentException("other must not be null");
        }
        if (other != this) {
            final long newPtr;
            synchronized (other.mLock) {
                newPtr = other.mNativeObject;
                other.setNativeObjectLocked(0);
            }

            synchronized (mLock) {
                if (mNativeObject != 0) {
                    nativeRelease(mNativeObject);
                }
                setNativeObjectLocked(newPtr);
            }
        }
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public void readFromParcel(Parcel source) {
        if (source == null) {
            throw new IllegalArgumentException("source must not be null");
        }

        synchronized (mLock) {
            //nativeReadFromParcel()将返回mNativeObject，或者创建新的本机Surface并在减少mNativeObject上的引用计数后返回它。
            // 无论哪种方式，没有必要在这里调用nativeRelease()

            mName = source.readString();
            setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
        }
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        if (dest == null) {
            throw new IllegalArgumentException("dest must not be null");
        }
        synchronized (mLock) {
            dest.writeString(mName);
            nativeWriteToParcel(mNativeObject, dest);
        }
        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
            release();
        }
    }

    @Override
    public String toString() {
        synchronized (mLock) {
            return "Surface(name=" + mName + ")/@0x" +
                    Integer.toHexString(System.identityHashCode(this));
        }
    }

    private void setNativeObjectLocked(long ptr) {
        if (mNativeObject != ptr) {
            if (mNativeObject == 0 && ptr != 0) {
                mCloseGuard.open("release");
            } else if (mNativeObject != 0 && ptr == 0) {
                mCloseGuard.close();
            }
            mNativeObject = ptr;
            mGenerationId += 1;
        }
    }

    private void checkNotReleasedLocked() {
        if (mNativeObject == 0) {
            throw new IllegalStateException("Surface has already been released.");
        }
    }

    /**
     * 提前分配缓冲区以避免渲染过程中的分配延迟
     * @hide
     */
    public void allocateBuffers() {
        synchronized (mLock) {
            checkNotReleasedLocked();
            nativeAllocateBuffers(mNativeObject);
        }
    }

    /**
     * 当Canvas无法使用{@link Surface＃lockCanvas}锁定时，或无法成功分配表面纹理时引发异常。
     */
    @SuppressWarnings("serial")
    public static class OutOfResourcesException extends RuntimeException {
        public OutOfResourcesException() {
        }
        public OutOfResourcesException(String name) {
            super(name);
        }
    }

    /**
     * 回旋转的人类可读表示
     *
     * @param rotation The rotation.
     * @return The rotation symbolic name.
     *
     * @hide
     */
    public static String rotationToString(int rotation) {
        switch (rotation) {
            case Surface.ROTATION_0: {
                return "ROTATION_0";
            }
            case Surface.ROTATION_90: {
                return "ROATATION_90";
            }
            case Surface.ROTATION_180: {
                return "ROATATION_180";
            }
            case Surface.ROTATION_270: {
                return "ROATATION_270";
            }
            default: {
                throw new IllegalArgumentException("Invalid rotation: " + rotation);
            }
        }
    }

    /**
     * 可以处理兼容模式的Canvas类。 这有两点不同。
     * 1.返回目标度量的宽度和高度，而不是本机。 例如，即使应用程序以WVGA高密度运行，画布也会返回320x480。
     * 2.除了如果矩阵看起来像从矩阵中获得的那样，应用比例缩放集合矩阵中的矩阵。
     * 对于应用程序使用getMatrix来保留原始矩阵，设置它自己的矩阵，然后设置原始矩阵，这是一种破解。
     * 没有适用于所有情况的完美解决方案，并且有很多情况下这种模式不起作用，但我们希望这适用于许多应用程序。
     *
     */
    private final class CompatibleCanvas extends Canvas {
        //记忆通过{@link getMatrix}获得的应用程序的临时矩阵
        private Matrix mOrigMatrix = null;

        @Override
        public void setMatrix(Matrix matrix) {
            if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
                // 如果不是兼容模式，则不要缩放矩阵，或者矩阵是从get Matrix获得的。
                super.setMatrix(matrix);
            } else {
                Matrix m = new Matrix(mCompatibleMatrix);
                m.preConcat(matrix);
                super.setMatrix(m);
            }
        }

        @SuppressWarnings("deprecation")
        @Override
        public void getMatrix(Matrix m) {
            super.getMatrix(m);
            if (mOrigMatrix == null) {
                mOrigMatrix = new Matrix();
            }
            mOrigMatrix.set(m);
        }
    }
}
